package com.cgg.stock.transaction.service.impl;

import com.cgg.stock.client.DealClient;
import com.cgg.stock.common.exception.MatchFailedException;
import com.cgg.stock.common.exception.PriceNotNullException;
import com.cgg.stock.common.pojo.Order;
import com.cgg.stock.common.pojo.TradeOrder;
import com.cgg.stock.common.util.RedisConstants;
import com.cgg.stock.transaction.service.ITransactionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.time.Duration;
import java.util.*;


@Service
public class TransactionServiceImpl implements ITransactionService {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private DealClient dealClient;

    /**
     * @Description: 限价卖出撮合
     * @param buyOrder 买订单
     */
    @Override
    public List<TradeOrder> processLimitBuy(Order buyOrder) throws Exception {
        List<TradeOrder> completeOrders = new ArrayList<>();
        String stockId = buyOrder.getStockId();
        BigDecimal buyPrice = buyOrder.getPrice();
        if (buyPrice == null){
            throw new PriceNotNullException();
        }

        //加锁
        String lock = (String) redisTemplate.opsForValue().get("lock");
        if (lock != null){
            throw new MatchFailedException();
        }
        redisTemplate.opsForValue().setIfAbsent("lock","正在撮合", Duration.ofSeconds(5));
        //1.获取卖出订单队列
        Set<Order> sellOrders = redisTemplate.opsForZSet().range(RedisConstants.SELLQUEUE + stockId,0,-1);
        if (!CollectionUtils.isEmpty(sellOrders)){
            //2.通过价格找匹配的订单（买入订单价格 >= 卖出订单价格）
            int buyNum = buyOrder.getNum();
            for (Order sellOrder : sellOrders) {
                if (buyPrice.compareTo(sellOrder.getPrice()) < 0){
                    return null;
                }
                //3.判断买入订单的数量 <= 卖出订单的剩余数量
                //    true,数量相减，再次判断剩余数量是否为0
                //    false,减掉剩余数量并把该订单移出卖出队列
                int sellNum = sellOrder.getNum();
                if (sellNum >= buyNum){
                    completeOrders.add(new TradeOrder(stockId,buyOrder.getId(),sellOrder.getId(),buyPrice,buyNum));

                    sellNum -= buyNum;
                    if (sellNum == 0){
                        sellOrders.remove(sellOrder);
                    }
                    return completeOrders;
                }else {
                    completeOrders.add(new TradeOrder(stockId,buyOrder.getId(),sellOrder.getId(),buyPrice,sellNum));

                    buyNum -= sellNum;
                    sellOrders.remove(sellOrder);
                }
            }
            //4.更新卖出队列
            for (Order sellOrder : sellOrders) {
                redisTemplate.opsForZSet().add(RedisConstants.SELLQUEUE + stockId,sellOrder,sellOrder.getPrice().doubleValue());
            }
        }
        //5.将剩下的订单放入买入队列
        redisTemplate.opsForZSet().add(RedisConstants.BUYQUEUE + stockId,buyOrder,buyPrice.doubleValue());
        //释放锁
        redisTemplate.delete("lock");
        //if (!CollectionUtils.isEmpty(completeOrders)){
        //    dealClient.dealOrder(completeOrders);
        //}
        return completeOrders;

    }

    /**
     * @Description: 限价卖出撮合
     * @param sellOrder 卖订单
     */
    @Override
    public List<TradeOrder> processLimitSell(Order sellOrder) throws Exception {
        List<TradeOrder> completeOrders = new ArrayList<>();
        String stockId = sellOrder.getStockId();
        BigDecimal sellPrice = sellOrder.getPrice();
        if (sellPrice == null){
            throw new PriceNotNullException();
        }

        String lock = (String) redisTemplate.opsForValue().get("lock");
        if (lock != null){
            throw new MatchFailedException();
        }
        redisTemplate.opsForValue().setIfAbsent("lock","正在撮合", Duration.ofSeconds(5));
        //1.获取买入订单队列
        Set<Order> orders = redisTemplate.opsForZSet().reverseRange(RedisConstants.BUYQUEUE + stockId,0,-1);
        Set<Order> buyOrders = new TreeSet<>((o1, o2) -> {
            if (o1.getPrice().compareTo(o2.getPrice()) == 0){
                return (int) (o1.getRegisterOrderTime().getTime() - o2.getRegisterOrderTime().getTime());
            }
            return o1.getPrice().compareTo(o2.getPrice());
        });
        for (Order buyOrder : orders) {
            buyOrders.add(buyOrder);
        }
        if (!CollectionUtils.isEmpty(buyOrders)){
            //2.通过价格找匹配的订单（买入订单价格 >= 卖出订单价格）
            int sellNum = sellOrder.getNum();
            for (Order buyOrder : buyOrders) {
                if (sellPrice.compareTo(buyOrder.getPrice()) > 0){
                    return null;
                }
                //3.判断卖出订单的数量 <= 买入订单的剩余数量
                //    true,数量相减，再次判断剩余数量是否为0
                //    false,减掉剩余数量并把该订单移出卖出队列
                int buyNum = sellOrder.getNum();
                if (buyNum >= sellNum){
                    completeOrders.add(new TradeOrder(stockId,buyOrder.getId(),sellOrder.getId(),buyOrder.getPrice(),sellNum));
                    buyNum -= sellNum;
                    if (buyNum == 0){
                        buyOrders.remove(sellOrder);
                    }
                    return completeOrders;
                }else {
                    completeOrders.add(new TradeOrder(stockId,buyOrder.getId(),sellOrder.getId(),buyOrder.getPrice(),buyNum));
                    sellNum -= buyNum;
                    buyOrders.remove(buyOrder);
                }
            }
            //4.更新买入队列
            for (Order buyOrder : buyOrders) {
                redisTemplate.opsForZSet().add(RedisConstants.SELLQUEUE + stockId,buyOrder,buyOrder.getPrice().doubleValue());
            }
        }
        //5.将剩下的订单放入卖出队列
        redisTemplate.opsForZSet().add(RedisConstants.SELLQUEUE + stockId,sellOrder,sellPrice.doubleValue());
        //释放锁
        redisTemplate.delete("lock");
        //if (!CollectionUtils.isEmpty(completeOrders)){
        //    dealClient.dealOrder(completeOrders);
        //}

        return completeOrders;
    }
}
